package threading;

// 【懒汉模式】的单例模式：提高效率

// 线程不安全只是在首次创建实例之前存在
// 此时直接加锁会导致后续线程已经安全的时候还在继续加锁
// 而加锁的开销其实还是挺大的，加锁可能会设计用户态到内存态之间的切换，这里的切换成本是比较高的

class SingleLazy {
    // 此时其实并没有真正创建实例对象

    // 存在指令重排序问题：加上volatile关键字
    // 既保证内存可见性，又禁止指令重排序
    private volatile static SingleLazy instance = null;

    // 首次使用到该实例的时候才去创建该实例对象
    public static SingleLazy getInstance() {
        // 加锁来解决线程不安全的问题！！
        // 该锁要加在load之前 也就是if之外，再注意加锁对象！！

        //在加锁的外层再加一层判定条件，符合条件才加锁->后续线程安全就不再加锁
        // 这里有两个判断的if一模一样，为啥要写两遍呢？
        // 这两个代码中间隔着一个加锁操作，这就意味着这两个if执行的时间间隔可能是“沧海桑田”
        // 两个if的结果可能是不同的：如同时调用两个线程，首先拿到的实例都是null，但是此时只有一个线程会先加锁
        // 对instance实例进行创建，而在该线程创建完成后，另一个线程加锁比较厚发现此时的instance已经创建不再空了
        // 就不会再次创建新实例对象了
        if(instance == null) {
            synchronized (SingleLazy.class) {
                if(instance == null) {
                    // 首次调用getInstance的时候才去创建对象
                    instance = new SingleLazy();
                }
            }
        }

        return instance;
    }

    // 构造方法设为私有
    private SingleLazy() {

    }
}

public class Demo2 {
    public static void main(String[] args) {
        //SingleLazy instance = new SingleLazy();  // 直接创建实例对象不行，因为构造方法私有
        // 而是调用方法
        SingleLazy instance = SingleLazy.getInstance();
        // 如果后续每人没用getInstance就把构造实例的过程给节省下来了
        //  即使后续有人调用，但是调用的实际比较晚则创建实例的时机也就比较迟，就和其他的耗时操作岔开了
        // （一般程序刚启动的时候要初始化的东西很多，系统资源比较紧张！）
    }
}
